//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __ELASTOS_SEGMENT_H__
#define __ELASTOS_SEGMENT_H__

//
// GDT
//
// number of entries
const uint_t c_uMaxNumberOfGdtEntries   = 1ul << 13;

//
// Segment Descriptor
//
// in u8Flag_0_15
enum
{
    SegDescFlag_Present             = __32BIT(7),

    SegDescFlag_DPL0                = 0x00,
    SegDescFlag_DPL1                = 0x20,
    SegDescFlag_DPL2                = 0x40,
    SegDescFlag_DPL3                = 0x60,

    SegDescFlag_Segment             = __32BIT(4),   // user segment

    SegDescFlag_Type_Executable     = __32BIT(3),

    // code segment (__Type_Executable)
    SegDescFlag_Type_Conforming     = __32BIT(2),
    SegDescFlag_Type_Readable       = __32BIT(1),

    // data segment (no __Type_Executable)
    SegDescFlag_Type_ExpandDown     = __32BIT(2),
    SegDescFlag_Type_Writable       = __32BIT(1),

    SegDescFlag_Type_Accessed       = __32BIT(0),

    // TSS
    SegDescFlag_Type_TSS            = 0x09,
    SegDescFlag_Type_Busy           = 0x02,

    // in u4Flag_16_23
    SegDescFlag_Granularity         = __32BIT(3),

    SegDescFlag_DB                  = __32BIT(2),

    SegDescFlag_AVL                 = __32BIT(0),
};

//
//  Segment Selector
//
#define KERNEL_PRIVILEGE                (0)
#define USER_PRIVILEGE                  (3)

typedef enum SelectorIndex
{
    SelectorIndex_Null                  = 0,
    SelectorIndex_KernelCode,
    SelectorIndex_KernelData,
    SelectorIndex_UserCode,
    SelectorIndex_UserData,
    SelectorIndex_TSS,

    NumberOfSelectors,
    NumberOfGdtEntries                  = NumberOfSelectors,
} SelectorIndex;

typedef uint16_t    selector_t;

#define SEGMENT_SELECTOR(index, ti, rpl) \
    ((selector_t)(((index) << 3) + ((ti) << 2) + (rpl)))

#define SEGMENT_SELECTOR_RPL(selector) \
    ((selector_t)(selector) & 0x3)

#define IS_KERNEL_SELECTOR(selector) \
    (KERNEL_PRIVILEGE == SEGMENT_SELECTOR_RPL(selector))

#define IS_USER_SELECTOR(selector) \
    (!IS_KERNEL_SELECTOR(selector))

#define IS_IN_KERNEL_MODE(pContext) \
    IS_KERNEL_SELECTOR((pContext)->cs)

#define IS_IN_USER_MODE(pContext) \
    IS_USER_SELECTOR((pContext)->cs)

#define GDT_SELETOR(index, rpl)             SEGMENT_SELECTOR((index), 0, (rpl))

#define NULL_SELECTOR \
    GDT_SELETOR(SelectorIndex_Null,         KERNEL_PRIVILEGE)
#define KERNEL_CODE_SELECTOR \
    GDT_SELETOR(SelectorIndex_KernelCode,   KERNEL_PRIVILEGE)
#define KERNEL_DATA_SELECTOR \
    GDT_SELETOR(SelectorIndex_KernelData,   KERNEL_PRIVILEGE)
#define USER_CODE_SELECTOR \
    GDT_SELETOR(SelectorIndex_UserCode,     USER_PRIVILEGE)
#define USER_DATA_SELECTOR \
    GDT_SELETOR(SelectorIndex_UserData,     USER_PRIVILEGE)
#define TSS_SELECTOR \
    GDT_SELETOR(SelectorIndex_TSS,          KERNEL_PRIVILEGE)

//
// IDT
//
// number of entries
const uint_t c_uNumberOfIdtEntries          = 0x100;

// flags
enum
{
    GateDescFlag_Present            = __32BIT(15),

    GateDescFlag_DPL0               = 0x0000,
    GateDescFlag_DPL1               = 0x2000,
    GateDescFlag_DPL2               = 0x4000,
    GateDescFlag_DPL3               = 0x6000,

    GateDescFlag_Type_Task          = 0x0500,
    GateDescFlag_Type_Interrupt_32  = 0x0e00,
    GateDescFlag_Type_Interrupt_16  = 0x0600,
    GateDescFlag_Type_Trap_32       = 0x0f00,
    GateDescFlag_Type_Trap_16       = 0x0700,
    GateDescFlag_Type_Call          = 0x0c00,
};

//
//  types
//
typedef struct DECL_PACKED SegmentDescriptor
{
    uint16_t    u16Limit_0_15;
    uint16_t    u16Base_0_15;
    uint8_t     u8Base_16_23;
    uint8_t     u8Flag_0_15;
    uint8_t     u4Limit_19_16 : 4;
    uint8_t     u4Flag_16_23  : 4;
    uint8_t     u8Base_24_31;
} SegmentDescriptor;

typedef struct DECL_PACKED GdtPointer
{
    uint16_t u16Limit;
    uint32_t u32Base;
} GdtPointer;

#define TSS_IOMAP_SIZE      (0x400 / CHAR_BIT)  // non-PNP ISA bus

typedef struct DECL_PACKED TaskStateSegment
{
    uint32_t    previousTaskLink;
    uint32_t    esp0;
    uint32_t    ss0;
    uint32_t    esp1;
    uint32_t    ss1;
    uint32_t    esp2;
    uint32_t    ss2;
    uint32_t    cr3;
    uint32_t    eip;
    uint32_t    eflags;
    uint32_t    eax;
    uint32_t    ecx;
    uint32_t    edx;
    uint32_t    ebx;
    uint32_t    esp;
    uint32_t    ebp;
    uint32_t    esi;
    uint32_t    edi;
    uint32_t    es;
    uint32_t    cs;
    uint32_t    ss;
    uint32_t    ds;
    uint32_t    fs;
    uint32_t    gs;
    uint32_t    ldtSelector;
    uint32_t    ioMapBaseAddress;
    uint8_t     ioMap[TSS_IOMAP_SIZE];
} TaskStateSegment;

EXTERN TaskStateSegment g_tss;

typedef struct DECL_PACKED GateDescriptor
{
    uint16_t    u16Offset_0_15;
    uint16_t    u16SegmentSelector;
    uint16_t    u16Flag;
    uint16_t    u16Offset_16_31;
} GateDescriptor;

typedef GdtPointer  IdtPointer;

typedef uint_t vector_t;

EXTERN void CDECL SetGlobalDescriptor(
        uint_t uIndex, SegmentDescriptor *pDescriptor);

EXTERN void SetGate(vector_t vector, uint16_t u16Flag, virtaddr_t va);

INLINE void CDECL SetInterruptGate(vector_t vector, virtaddr_t va)
{
    SetGate(vector,
        GateDescFlag_Present + GateDescFlag_DPL0
            + GateDescFlag_Type_Interrupt_32,
        va);
}

INLINE void CDECL SetTrapGate(vector_t vector, virtaddr_t va)
{
    SetGate(vector,
        GateDescFlag_Present + GateDescFlag_DPL0
            + GateDescFlag_Type_Trap_32,
        va);
}

INLINE void CDECL SetSystemGate(vector_t vector, virtaddr_t va)
{
    SetGate(vector,
        GateDescFlag_Present + GateDescFlag_DPL3
            + GateDescFlag_Type_Trap_32,
        va);
}

INLINE void CDECL SetCallGate(vector_t vector, virtaddr_t va)
{
    SetGate(vector,
        GateDescFlag_Present + GateDescFlag_DPL3
            + GateDescFlag_Type_Call,
        va);
}

#endif //__ELASTOS_SEGMENT_H__
